use std::{
    fmt::{self, format},
    ops::{Add, Div, Mul, Rem, Sub},
};

use super::callable::Callable;



#[derive(Debug, PartialEq, Clone)]
pub enum Object {
    Number(f64),
    String(String),
    Bool(bool),
    Nil,
    Function(Callable),
}

impl fmt::Display for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Number(x) => write!(f, "{x}"),
            Self::String(x) => write!(f, "{x}"),
            Self::Bool(x) => {
                if *x {
                    write!(f, "true")
                } else {
                    write!(f, "false")
                }
            }
            Self::Function(_) => write!(f,"<func>"),

            Self::Nil => write!(f, "nil"),
            _ => panic!("Should not be trying to print this"),
        }
    }
}

impl Sub for Object {
    type Output = Object;

    fn sub(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (Object::Number(x), Object::Number(y)) => Object::Number(x - y),
            _ => Object::Nil,
        }
    }
}
impl Div for Object {
    type Output = Object;

    fn div(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (Object::Number(x), Object::Number(y)) => Object::Number(x / y),
            _ => Object::Nil,
        }
    }
}
impl Mul for Object {
    type Output = Object;

    fn mul(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (Object::Number(x), Object::Number(y)) => Object::Number(x * y),
            _ => Object::Nil,
        }
    }
}
impl Add for Object {
    type Output = Object;

    fn add(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (Object::Number(x), Object::Number(y)) => Object::Number(x + y),
            (Object::String(x), Object::String(y)) => Object::String(format!("{}{}", x, y)),

            _ => Object::Nil,
        }
    }
}

impl Rem for Object {
    type Output = Object;

    fn rem(self, rhs: Self) -> Self::Output {
        match (self, rhs) {
            (Object::Number(x), Object::Number(y)) => Object::Number(x % y),
            _ => Object::Nil,
        }
    }
}

impl PartialOrd for Object {
    // fn lt(&self, other: &Self) -> bool {
    //     matches!(self.partial_cmp(other), Some(Less))
    // }

    // fn le(&self, other: &Self) -> bool {
    //     matches!(self.partial_cmp(other), Some(Less | Equal))
    // }

    // fn gt(&self, other: &Self) -> bool {
    //     matches!(self.partial_cmp(other), Some(Greater))
    // }

    // fn ge(&self, other: &Self) -> bool {
    //     matches!(self.partial_cmp(other), Some(Greater | Equal))
    // }

    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        match (self, other) {
            (Object::Number(x), Object::Number(y)) => x.partial_cmp(y),
            (Object::Bool(x), Object::Bool(y)) => x.partial_cmp(y),
            _ => None,
        }
    }
}

